/***************************************************************************************************
 *Copyright(C),2010-2016,Sumscope
 *FileName	:  DickChecker.cpp
 *Author	:  scofined.qi
 *Version	:  1.0
 *Date		:  2019/06/14
 *Desc		:  //用于主要说明此程序文件完成的主要功能
 *Relation :
 *Others	:  //其他内容说明
 *Function :  //主要函数列表，每条记录应包含函数名及功能简要说明
 *History	:
 * //修改历史记录列表，每条修改记录应包含修改日期、修改者及修改内容简介
 ***************************************************************************************************/
#include "DictChecker.h"

#include <bondlib/BondContainer.h>
#include <core/sharedata/login_data.h>
#include <core/strings/Rand.h>
#include <core/time/time.h>
#include <qbtools/message/MessagePoster.h>
#include <uam/UserAccountManager.h>

#include "nzip.h"
#include "qbkernel/include/qb_base.h"
#include "qbprotocol/include/rdMessage.h"
#include "qbprotocol/include/rdProto.h"

#ifndef max
#define max(a, b) ((a) > (b)) ? (a) : (b)
#endif

#ifdef WIN32
#else
//#define _TRUNCATE ((size_t)-1)
//#define FIELD_OFFSET(type, field) ((long)(long)&(((type*)0)->field))
#define RTL_FIELD_SIZE(type, field) (sizeof(((type*)0)->field))
#endif

static bool CopyPatchName(QBDictCheck_Ack* ptr, const char* name, int size) {
#ifdef WIN32
  __try {
#else
  try {
#endif
    int capcity = 32;  // RTL_FIELD_SIZE(QBDictCheck_Ack,m_PatchName)
    char* dest = ptr->m_PatchName;
    int len = size >= capcity ? (capcity - 1) : size;
    memset(dest, 0, capcity);
    if (name && len > 0) memcpy(dest, name, len);
  }
#ifdef WIN32
  __except (1)
#else
  catch (...)
#endif
  {
    return false;
  }
  return true;
}

static void* DecodeDictCheck(int funcId, const ProtoMessage& msg, void* toDel) {
  if (funcId != E_FID_QB_DICT_CHECK) return nullptr;

  if (toDel != nullptr) {
    QBDictCheck_Ack* pack = static_cast<QBDictCheck_Ack*>(toDel);
    delete pack;
    return nullptr;
  }

  size_t size = 0;
  const char* md5 = nullptr;
  const char* name = nullptr;
  QBDictCheck_Ack* ptr = new QBDictCheck_Ack();
  if (msg.RefString("PatchName", name, size) && name && size > 0) {
    CopyPatchName(ptr, name, size);
  }
  if (msg.RefString("Md5", md5, size) && md5 && size > 0) {
    memset(ptr->m_Md5, 0, RTL_FIELD_SIZE(QBDictCheck_Ack, m_Md5));
    FIELDCOPYN_NOZEROEND(ptr->m_Md5, md5, size);
  }
  msg.GetInt32("retcode", ptr->m_retcode);
  msg.GetInt32("UpdateId", ptr->m_UpdateId);
  return ptr;
}
namespace qb {
namespace ht {
const char* GetDictTypeVersion(kDictType type) {
  BASSERT(type >= 0 && type < kDictTypeCount);
  const DictDesc& dd = DictDesc::GetStockDictDesc(type);
  return dd.m_version.c_str();
}
kDictType GetDictTypeByVersion(const char* patchName) {
  for (int i = 0; i < kDictTypeCount; i++) {
    const DictDesc& dd = DictDesc::GetStockDictDesc(i);
    if (strcmp(dd.m_version.c_str(), patchName) == 0) return (kDictType)i;
  }
  return kDictTypeCount;
}

bool DickChecker::ZipBuffer::Reset(int capcity) {
  if (m_buffer == 0 || m_capcity < capcity) {
    if (m_buffer) delete[] m_buffer;
    m_buffer = new (std::nothrow) char[capcity];
    if (m_buffer == 0) return false;
    m_capcity = capcity;
  }
  memset(m_buffer, 0, m_capcity);
  return true;
}

DickChecker::DickChecker(DictManager& dictManager)
    : m_dict_manager(dictManager),
      m_is_checking(false),
      m_registered(false),
      m_seq_index(0),
      m_seq_end(0),
      m_check_count(0),
      m_current_dict(kDictType(0)),
      m_emergent_check(30000)  // 30秒
{
  // kProfileDictCheckRandRange控制从字典线程开始起,多少分钟后开始字典检测
  // kProfileDictCheckFirstWait控制额外的等待时间的随机值的选取范围,避免流量集中在一个时间点上
  m_emergent_check =
      30000;           // AfxGetConfig().GetInt(kProfileDictEmergentCheckTime);
  int rand_gap = 10;   // AfxGetConfig().GetInt(kProfileDictCheckRandRange);
  int first_wait = 5;  // AfxGetConfig().GetInt(kProfileDictCheckFirstWait);
  int randwait = base::Rand::RandInt(0, rand_gap) * 60000;  // 毫秒
  int minisec = first_wait * 60000 + randwait;              // 毫秒
  m_check_zip = false;               // BOOLTEST(kProfileDictCheckZipValidate);
  m_next_check += minisec * 1000.0;  // 微秒

  qbBase::getMessageBond()->RegisterDecodeFunc(E_FID_QB_DICT_CHECK,
                                               DecodeDictCheck);
  qbBase::getMessageBond()->RegisterMsgProcessor(E_FID_QB_DICT_CHECK, this);
}

DickChecker::~DickChecker() {
  if (qbBase::getMessageBond())
    qbBase::getMessageBond()->UnRegisterMsgProcessor(E_FID_QB_DICT_CHECK, this);
}

void DickChecker::SetNextCheck(HighResTime time) { m_next_check = time; }

void DickChecker::SetCheckLatter(const char* cause) {
  // 将下次检测时间调节为30秒之后,具体值见m_emergent_check
  HighResTime cur;
  int minisec = m_emergent_check;  // 30秒
  cur += minisec * 1000.0;         // 微秒
  int nbonds = CBondContainer::instance().GetSize();
  SetNextCheck(
      cur);  // 强行更改下次检测时间为kProfileDictEmergentCheckTime毫秒之后
  log_info("[DICT][CHECK]%s 开启紧急字典检查:%s,bonds:%8d", cause ? cause : "",
           cur.ToString().c_str(), nbonds);
}

bool DickChecker::StartCheck(bool autoCheck /*= true*/) {
  if (m_is_checking) return false;
  HighResTime curtime;
  if (curtime < m_next_check) return false;

  m_check_count++;
  m_sequences.clear();
  m_seq_index = m_seq_end = 0;
  m_current_dict = kDictType(0);

  // 将上一次失败后服务器给定的updateid都记录起来
  // 如果第二次检查时,发现上次依旧是kCheckLevelByUpdateId,并且updateid>0
  // 那么升级到kCheckLevelBySequence,这会触发字典重建
  for (auto it = m_failed_results.begin(); it != m_failed_results.end(); ++it) {
    BASSERT(it->second.level > kCheckLevelNoError);
    if (m_last_results.find(it->first) != m_last_results.end())
      it->second.cerror++;
    else
      it->second.cerror = 0;
  }
  m_last_results.clear();
  std::swap(m_last_results, m_failed_results);
  m_requests.clear();
  m_is_checking = true;
  m_nzip_buffer.Clear();
  m_zlib_buffer.Clear();
  m_start_time = HighResTime();
  m_dict_manager.GetDictSequence(m_current_dict, m_sequences);

  PrintDictState(true);
  SendCheckRequest();
  if (IsDictFinished()) {
    m_is_checking = false;
    UpdateNextTime();
  }
  return true;
}

void DickChecker::PrintDictState(bool start) const {
  log_info("[DICT][CHECK]第%3d 次字典检测%s 错误:%2d/%2d", m_check_count,
           start ? "开始" : "结束", m_failed_results.size(), kDictTypeCount);
  for (auto it = m_failed_results.begin(); it != m_failed_results.end(); ++it) {
    kDictType type = (kDictType)it->first;
    int updateid = it->second.ack.m_UpdateId;
    kCheckLevel level = (kCheckLevel)it->second.level;
    log_info(
        "[DICT][CHECK]字典:%32s  检查等级:%12s  UpdateId:%6d 连续次数:%2d %s",
        GetDictTypeVersion(type), GetLevelName(level), updateid,
        it->second.cerror, level != kCheckLevelNoError ? "******" : "      ");
  }
}

bool DickChecker::CheckFinished(long reqid, const QBDictCheck_Ack& ack) {
  // 返回true表示检测全部结束
  // 返回false表示检测还在进行中
  // 收到一个检测响应包之后,还会继续发送后续的检测请求包
  // 单个包请求验证成功或者错误,依旧返回false,全部结束后,调用GetCheckResult即可
  BASSERT(m_is_checking);
  RequestMap::iterator it = m_requests.find(reqid);
  if (it == m_requests.end()) {
    log_error(
        "[DICT][CHECK][应答][找不到]reqid:%6d can not be found, PatchName:%s",
        reqid, ack.m_PatchName);
    return false;
  }
  Request& req = it->second;
  req.ack = ack;
  req.ack_received = true;
  if (ack.m_retcode == 0) {
    req.has_error = true;
    AddDictCheckInfo(ack, 0);
  } else {
    // 没有就是正确
    // kDictType type = GetDictTypeByVersion(ack.m_PatchName);
    // if (type < kDictTypeCount)
    //	SetCheckLevel(type, kCheckLevelNoError);
  }
  log_info(
      "[DICT][CHECK][应答]reqid:%6d dict:%16s retcode:%6d(%s) last:%6dms "
      "UpdateId:%8d  Md5:%36s  PatchName:%s",
      reqid, GetDictTypeVersion(req.type), ack.m_retcode,
      (ack.m_retcode == 1) ? "成功" : "失败", req.req_time.elapsed().microsec(),
      ack.m_UpdateId, ack.m_Md5, ack.m_PatchName);

  SendCheckRequest();
  if (IsDictFinished()) {
    m_is_checking = false;
    UpdateNextTime();
    return true;
  }
  return false;
}

void DickChecker::UpdateNextTime() {
  // 在默认情况下(不对zip数据进行验证),会将下一次检测时间设置为
  // 延后一天,并将检测时间写入到配置文件dictconfig.ini中
  HighResTime cur = HighResTime();
  HighResTime check_time = m_next_check;

  int64_t oneday = 24 * 3600 * 1000000.0;  // 微秒
  HighResTime today = cur.today();
  HighResTime tomorrow((int64_t)today + oneday);
  tomorrow += m_next_check.todaypassed();
  SetNextCheck(tomorrow);  // 下次检测时间,一天只检测一次

  HighResTime elased = cur - m_start_time;
  log_info("[DICT][CHECK]本次检测时间:%s 总耗时:%d 下次字典检测时间为:%sus",
           check_time.ToString().c_str(), elased.microsec(),
           m_next_check.ToString().c_str());
}

void DickChecker::CancelCheck() {
  m_current_dict = (kDictType)0;
  m_seq_index = m_seq_end = 0;
  m_requests.clear();
  m_nzip_buffer.Clear();
  m_zlib_buffer.Clear();
  m_is_checking = false;
  log_error("[DICT] cancel check,clear all requests");
}

bool DickChecker::HasServerError() const {
  auto it = m_failed_results.begin();
  for (; it != m_failed_results.end(); ++it) {
    if (it->second.level == kCheckLevelServerError) return true;
  }
  return false;
}
int DickChecker::GetLastUpdateId(kDictType type) const {
  auto it = m_last_results.find(type);
  if (it != m_last_results.end() && it->second.level == kCheckLevelByUpdateId &&
      it->second.ack.m_UpdateId > 0)
    return it->second.ack.m_UpdateId;
  return -1;
}

void DickChecker::GetCheckResult(FailedResult& failedResults) {
  // 如果有错误,将会触发自动检查
  failedResults.clear();
  for (auto it = m_failed_results.begin(); it != m_failed_results.end(); ++it) {
    DictCheckInfo& info = it->second;
    kDictType type = info.type;
    if (type == kDictTypeMacroIndex)  // 忽略
      continue;
    if (info.level == kCheckLevelNoError)  // 忽略
      continue;
    if (info.level == kCheckLevelServerError)  // 忽略
    {
      log_error(
          "[DICT][CHECK][SERVER][IGNORE]字典%s "
          "kCheckLevelServerError,连续错误次数:%d",
          info.ack.m_PatchName, info.cerror);
      continue;
    }

    // 两条规则
    // 同为ByUpdateId,本次的UpdateID不能比上次高,否则提升level
    // 同为BySequence,本次提升level为ServerError
    kCheckLevel lastLevel = GetLastCheckLevel(type);
    if (lastLevel == kCheckLevelServerError && info.level != kCheckLevelNoError)
      continue;
    int lastUpdateId = GetLastUpdateId(type);
    if (info.level == kCheckLevelByUpdateId) {
      if (lastLevel == info.level && lastUpdateId > 0) {
        if (info.ack.m_UpdateId <= lastUpdateId)
          info.level = kCheckLevelBySequence;
      } else if (lastLevel == kCheckLevelBySequence && info.cerror >= 2)
        info.level = kCheckLevelServerError;
    } else if (info.level == kCheckLevelBySequence) {
      if (lastLevel == info.level) {
        info.level = kCheckLevelServerError;
      }
    }
    if (info.level == kCheckLevelBySequence) {
      info.nerror = 1;
      info.ack.m_UpdateId = 0;
      FIELDCOPY(info.ack.m_Md5, "");
    } else if (info.level == kCheckLevelServerError) {
      log_error(
          "[DICT][CHECK][SERVER][IGNORE]字典%s "
          "kCheckLevelServerError,连续错误次数:%d",
          info.ack.m_PatchName, info.cerror);
      continue;
    }
    failedResults[type] = info;
  }
}

bool DickChecker::IsDictFinished() const {
  return m_current_dict >= kDictTypeCount;
}
DickChecker::kCheckLevel DickChecker::GetLevel(kDictType type,
                                               bool last) const {
  if (last)
    return GetLastCheckLevel(type);
  else
    return GetCheckLevel(type);
}
DickChecker::kCheckLevel DickChecker::GetCheckLevel(kDictType dict) const {
  auto it = m_failed_results.find(dict);
  if (it == m_failed_results.end()) return kCheckLevelNoError;
  return (kCheckLevel)it->second.level;
}

DickChecker::kCheckLevel DickChecker::GetLastCheckLevel(kDictType dict) const {
  auto it = m_last_results.find(dict);
  if (it == m_last_results.end()) return kCheckLevelNoError;
  return (kCheckLevel)it->second.level;
}

const char* DickChecker::GetLevelName(kCheckLevel level) const {
  static const char* names[kCheckLevelCount + 1] = {
      "0:数据正确", "1:数据不同步", "2:序号不同步", "3:数据源错误",
      "4:非法状态"};
  return names[level];
}

void DickChecker::ClearCheckLevel(kDictType dict) {
  auto it = m_failed_results.find(dict);
  if (it != m_failed_results.end()) {
    it->second.level = kCheckLevelNoError;
    it->second.nerror = 0;
  }
}

void DickChecker::SetCheckLevel(kDictType dict, kCheckLevel level) {
  // 如果上次出错,那么需要提升错误等级
  // 错误不能降级,降级只能在ClearCheckLevel进行
  kCheckLevel last = GetLastCheckLevel(dict);
  kCheckLevel newLevel = level;
  if (level != kCheckLevelNoError &&
      (last == kCheckLevelByUpdateId || last == kCheckLevelBySequence)) {
    uint32_t uLevel = max(level, last + 1);
    ;
    newLevel = (kCheckLevel)uLevel;
  }

  auto it = m_failed_results.find(dict);
  if (it != m_failed_results.end() && it->second.level < newLevel) {
    it->second.level = newLevel;
  }

  if (last != newLevel) {
    log_info("[DICT][CHECK]%16s UPGRADE LEVEL FROM %s to %s",
             GetDictTypeVersion(dict), GetLevelName(last),
             GetLevelName(newLevel));
  }
}

STDMETHODIMP_(void)
DickChecker::OnResponse(THIS_ int32_t funcId, const SSAckMsg& msg) {
  if (funcId != E_FID_QB_DICT_CHECK) return;

  void* pdata = (void*)&msg;
  DictUpdateManager::instance().OnCheckReceived(pdata, nullptr);
}

void DickChecker::AddDictCheckInfo(const QBDictCheck_Ack& ack, int nerror) {
  // nerror>0表示本地序号检测失败,<=0表示向服务器查询知道失败
  DictCheckInfo info;
  info.ack = ack;
  info.nerror = nerror;

  info.level = nerror > 0 ? kCheckLevelBySequence : kCheckLevelByUpdateId;
  info.type = GetDictTypeByVersion(ack.m_PatchName);

  auto lastit = m_last_results.find(info.type);
  info.cerror = lastit == m_last_results.end() ? 0 : lastit->second.cerror;
  if (info.type < 0 || info.type >= kDictTypeCount) {
    log_error("[DICT][CHECK]invalid patch name:%s", ack.m_PatchName);
    return;
  }

  auto it = m_failed_results.find(info.type);
  if (it == m_failed_results.end()) {
    m_failed_results[info.type] = info;
  } else {
    DictCheckInfo& exist = it->second;
    exist.nerror += nerror;
    if (exist.ack.m_UpdateId < info.ack.m_UpdateId)  // 谁小取谁
    {
      exist.ack.m_UpdateId = info.ack.m_UpdateId;
      FIELDCOPY(exist.ack.m_Md5, info.ack.m_Md5);
    }
    if (exist.level < info.level)  // 谁大取谁
      exist.level = info.level;
  }
  // SetCheckLevel(info.type, (kCheckLevel)info.level);
}

bool DickChecker::CheckZip(char* buf, int length, int originalSize,
                           const std::vector<int>& sequences, int start,
                           int end) {
  BASSERT(buf != NULL && length > 0 && length < originalSize);
  BASSERT(start >= 0 && start < end && end <= (int)sequences.size());
  int seqerr = 0;
  for (int i = start + 1; i < end; i++) {
    if (sequences[i - 1] >= sequences[i]) seqerr++;
  }
  BASSERT(seqerr <= 0);
  ZipBuffer unzipbuf;
  unzipbuf.Reset(originalSize);
  int unzip_size = 0;
  if (!rdMessage::bUnZip(buf, length, unzipbuf, &unzip_size)) {
    log_error(
        "[DICT][CHECK]rdMessage::bUnZip failed for buffer:0x%x  length:%d", buf,
        length);
    return false;
  }

  std::vector<int> numbers;
  int num_count = 0, num_min = 0, num_max = 0;
  if (!SequenceZip4::GetPackInfo(unzipbuf, unzip_size, num_count, num_min,
                                 num_max)) {
    log_error("[DICT][CHECK]GetPackInfo failed");
    return false;
  }
  SequenceZip4::Uncompress(unzipbuf, unzip_size, numbers);
  if (numbers.size() != end - start) {
    log_error("[DICT][CHECK]Uncompress numbers count:%d != %d", numbers.size(),
              end - start);
    return false;
  }
  int nerror = 0;
  for (int i = start; i < end; i++) {
    if (numbers[i - start] != sequences[i]) nerror++;
  }
  if (nerror > 0) {
    log_error("[DICT][CHECK]Uncompress numbers failed, nerror:%d", nerror);
    return false;
  }
  return true;
}

bool DickChecker::UpdateSequences() {
  // 根据当前字典类型m_current_dict,以及当前进度[m_seq_index,m_seq_index)
  // 来更新当前字典的每条记录的序号数组
  // kDictTypeMacroIndex 字典不做检查,空字典不做检查
  // 只有当序号出错的时候,才会返回false
  m_seq_index = m_seq_end;
  if (m_current_dict >= kDictTypeCount) return true;

  if (m_seq_index >= (int)m_sequences.size()) {
    m_sequences.clear();
    while (m_sequences.empty())  // 空就忽略自动检测下一个
    {
      m_current_dict = (kDictType)(m_current_dict + 1);
      if (m_current_dict == kDictTypeMacroIndex)
        m_current_dict = (kDictType)(m_current_dict + 1);
      m_seq_index = m_seq_end = 0;
      if (m_current_dict >= kDictTypeCount) return true;
      m_dict_manager.GetDictSequence(m_current_dict, m_sequences);
      if (m_sequences.empty()) {
        log_error(
            "[DICT][CHECK]dict:%16s sequences_size:%6d  range:[%6d-%6d]  "
            "size:%6d compress_ratio:%2.4f",
            GetDictTypeVersion(m_current_dict), m_sequences.size(), -1, -1, 0,
            0.0);
      }
    }
  }

  // 检查序号,m_sequences已经是递增排序,可能有前后相同的情况
  BASSERT(m_seq_index < (int)m_sequences.size() && m_sequences.size() > 0);
  BASSERT(m_current_dict < kDictTypeCount);
  int nerror = 0, ncount = m_sequences.size();
  for (int i = 1; i < ncount; i++) {
    if (m_sequences[i - 1] >= m_sequences[i]) nerror++;
  }

  if (nerror <= 0) return true;

  // 出现了严重错误,此字典需要全部重拉,伪造错误记录
  // 错误记录的m_UpdateId用于指示后续重拉字典时从此m_UpdateId开始
  BASSERT(m_current_dict < kDictTypeCount);
  QBDictCheck_Ack ack;
  memset(&ack, 0, sizeof(QBDictCheck_Ack));
  ack.m_retcode = 0;
  ack.m_UpdateId = 0;
  FIELDCOPY(ack.m_PatchName, GetDictTypeVersion(m_current_dict));
  log_error(
      "[DICT][CHECK]%2d  字典:%16s 的序号出现 %3d 个错误,准备全量重拉字典",
      m_failed_results.size(), GetDictTypeVersion(m_current_dict), nerror);
  AddDictCheckInfo(ack, nerror);
  return false;
}

int DickChecker::ZipSequenceRange(int& zsize, int& nsize) {
  // 由于网络包大小有限制,需要自动调节,这里,最大尺寸控制为不超过30kb,
  // 将m_sequences中从m_seq_index开始到末尾的整数压缩成包,发送给服务器
  // 第一步,将严格递增序号数组使用自研的SequenceZip4::Compress压缩
  // 第二部,将第一步压缩后的数据再次使用rdMessage::bZip压缩
  // 如果最终尺寸超过max_zip_size,则选取范围减半,最终必定成功
  // 如果m_check_zip开启后,则对压缩数据进行检查
  const int max_zip_size = 30 * 1024;
  int max_index_end = m_sequences.size();
  const int* uniqAscendNumbers = &m_sequences[m_seq_index];
  int nzip_size = 0, zlib_size = 0;
  int nzip_capcity = 0, zlib_capcity = Y_RD_SERVICE_MESSAGE_BLOCK_LEN;
  int try_count = 0;
  m_zlib_buffer.Reset(Y_RD_SERVICE_MESSAGE_BLOCK_LEN);
  do {
    try_count++;
    nzip_capcity = (max_index_end - m_seq_index) * sizeof(int);
    nzip_capcity = nzip_capcity < zlib_capcity ? zlib_capcity : nzip_capcity;
    m_nzip_buffer.Reset(nzip_capcity);
    nzip_size =
        SequenceZip4::Compress(uniqAscendNumbers, max_index_end - m_seq_index,
                               m_nzip_buffer, nzip_capcity);

    zlib_size = zlib_capcity;
    if (!rdMessage::bZip(m_nzip_buffer, nzip_size, m_zlib_buffer, &zlib_size)) {
      log_error("[DICT]rdMessage::bZip failed,size:%d", nzip_size);
      return -1;
    }
    if (zlib_size < max_zip_size) break;
    max_index_end = m_seq_index + (max_index_end - m_seq_index) / 2;
  } while (true);
  BASSERT(max_index_end > m_seq_index);
  double ratio = (max_index_end - m_seq_index) * sizeof(int) * 1.0 / zlib_size;
  if (m_check_zip)
    CheckZip(m_zlib_buffer, zlib_size, nzip_size, m_sequences, m_seq_index,
             max_index_end);
  zsize = zlib_size;
  nsize = nzip_size;
  return max_index_end;
}

bool DickChecker::SendCheckRequest() {
  if (IsDictFinished()) return true;

  if (!UpdateSequences()) {
    // m_seq_end = m_sequences.size();
    CollectSeqFailure();
    m_is_checking = false;
    BASSERT(m_failed_results.size() > 0);
    log_error("[DICT][CHECK]当前字典检查由于出现%2d 个错误,已经取消",
              m_failed_results.size());
    return false;
  }
  if (m_current_dict >= kDictTypeCount) return true;

  DWORD tick = base::GetHighResTime();
  int nzip_size = 0;  // 第一层压缩后的大小
  int zlib_size = 0;  // 最终压缩后的大小(第二层压缩)
  int max_index_end = ZipSequenceRange(zlib_size, nzip_size);
  if (max_index_end < 0) return false;

  BASSERT(max_index_end <= (int)m_sequences.size());
  BASSERT(zlib_size > 0 && nzip_size > 0);

  SSRequestMsg Req;
  memset(&Req, 0, sizeof(SSRequestMsg));
  Req.m_MsgType = SSReqType_Message;
  Req.m_FuncID = E_FID_QB_DICT_CHECK;

  QBDictCheck_Req req;
  FIELDCOPY(req.m_PatchName, GetDictTypeVersion(m_current_dict));
  req.m_Pack = (char*)m_zlib_buffer;
  req.m_PackLength = zlib_size;
  req.m_Min = m_sequences[m_seq_index];
  req.m_Max = m_sequences[max_index_end - 1];

  Req.m_RequestID = 0;
  Req.m_pRequest = &req;

  MessagePoster poster(&Req, [](proto::Message& msg, SSRequestMsg* stReq) {
    proto::VariantMap submsg;
    QBDictCheck_Req* pdata = (QBDictCheck_Req*)stReq->m_pRequest;
    submsg.SetString("PatchName", pdata->m_PatchName);
    submsg.SetRaw("Pack", pdata->m_Pack, pdata->m_PackLength);
    submsg.SetInt32("PackLength", pdata->m_PackLength);
    submsg.SetInt32("Min", pdata->m_Min);
    submsg.SetInt32("Max", pdata->m_Max);

    // 以下是测试字段,服务器用于辅助调试
    submsg.SetString("ip", SSUserAccountManager::instance().GetUserAccount());
    msg.SetMap("QBDictCheck_Req", submsg);
  });

  long id = poster.send();

  double ratio =
      zlib_size == 0
          ? 0
          : ((max_index_end - m_seq_index) * sizeof(int) * 1.0 / zlib_size);
  log_info(
      "[DICT][CHECK][请求]reqid:%6d dict:%16s sequences_size:%6d  "
      "range:[%6d-%6d]  size:%6d compress_ratio:%2.4f ziptime:%6dms",
      id, GetDictTypeVersion(m_current_dict), m_sequences.size(),
      m_sequences[m_seq_index], m_sequences[max_index_end - 1], zlib_size,
      ratio, base::GetHighResTime() - tick);

  req.m_Pack = nullptr;
  m_seq_end = max_index_end;
  m_dict_manager.SaveSequences(m_sequences, m_current_dict, "dictcheck",
                               m_check_count);
  CacheRequest(nzip_size, zlib_size, id, tick);
  return true;
}
void DickChecker::CollectSeqFailure() {
  // 一旦一个出错,尝试检查后续字典中,是否也有类似的序号错误
  int index = 0;
  while (m_current_dict < kDictTypeCount) {
    m_seq_end = m_sequences.size();
    UpdateSequences();
    index++;
    BASSERT(index <= kDictTypeCount);
    if (index >= kDictTypeCount) break;
  }
}
void DickChecker::CacheRequest(int nsize, int zsize, long id, DWORD tick) {
  Request local_req;
  local_req.type = m_current_dict;
  local_req.seq_count = m_sequences.size();
  local_req.index_start = m_seq_index;
  local_req.index_count = m_seq_end - m_seq_index;
  local_req.req_id = id;
  local_req.ack_received = false;
  local_req.has_error = false;
  local_req.zip_time = base::GetHighResTime() - tick;
  local_req.nzip_size = nsize;
  local_req.zlib_size = zsize;
  //	local_req.req = req;
  local_req.req_time = HighResTime();
  m_requests[id] = local_req;
}

/*	long DickChecker::SendRequestBuffer(QBDictCheck_Req& req)
            {
                    SSRequestMsg reqData;
                    memset(&reqData, 0, sizeof(reqData));
                    reqData.m_MsgType = SSReqType_Message;
                    reqData.m_FuncID = E_FID_QB_DICT_CHECK;
                    reqData.m_RequestID = 0;
                    reqData.m_pRequest = &req;
                    AfxGetBondReceiver().RequestQpid(&reqData, NULL);
                    if (!m_registered)
                    {
                            m_registered = true;
                            AfxGetBondReceiver().AddRcvRegistryDataWnd(m_wnd_handle,
   E_FID_QB_DICT_CHECK, NULL);
                    }
                    return reqData.m_RequestID;
    }*/

}  // namespace ht
}  // namespace qb
